package com.intro.database.dao;

import static org.hibernate.criterion.Example.create;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import com.intro.util.Constants;
import com.intro.util.HibernateSessionFactory;
import com.intro.util.Page;

public class QueryDao  {

	private static final Logger log = LoggerFactory.getLogger(QueryDao.class);

	/**
	 * **********************************
	 * ********************************** 
	 * 常规查询
	 * ********************************** 
	 * **********************************
	 */

	/**
	 * 通过主键ID获取对象信息
	 * 
	 * @param <T>
	 * @param cls
	 * @param id
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T findById(Class<T> cls, Serializable id) {
		log.debug("getting " + cls.getName() + "instance with id: " + id);
		try {
			getSession().clear();
			T instance = (T) getSession().get(cls.getName(), id);
			return instance;
		} catch (Exception re) {
			log.error("get failed", re);
			throw new RuntimeException(re);
		}
	}
	
	/**
	 * 获取唯一字段对象
	 * @param <T>
	 * @param cls
	 * @param key
	 * @param value
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T findByUniqe(Class<T> cls, String key, Object value) {
		try {
			getSession().clear();
			String queryString = "from " + cls.getName()
					+ " as model where model." + key + " = ?";
			Query queryObject = getSession().createQuery(queryString);
			queryObject.setParameter(0, value);
			return (T) queryObject.uniqueResult();
		} catch (Exception re) {
			log.error("get failed", re);
			throw new RuntimeException(re);
		}
	}

	/**
	 * 获取指定属性值的对象列表
	 * 
	 * @param <T>
	 * @param cls
	 * @param key
	 * @param value
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> findByProperty(Class<T> cls, String key, Object value) {
		log.debug("find " + cls.getName() + "by (" + key + "=" + value + ")");
		try {
			getSession().clear();
			String queryString = "from " + cls.getName()
					+ " as model where model." + key + " = ?";
			Query queryObject = getSession().createQuery(queryString);
			queryObject.setParameter(0, value);
			return queryObject.list();
		} catch (Exception re) {
			log.error("find " + cls.getName() + " by property " + key
					+ " failed", re);
			throw new RuntimeException(re);
		}
	}

	private Session getSession() {
		// TODO Auto-generated method stub
		return HibernateSessionFactory.getSession();
	}

	/**
	 * 通过Example方式查询对象列表
	 * 
	 * @param <T>
	 * @param cls
	 * @param exampleObj
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> findByExample(Class<T> cls, T exampleObj) {
		log.debug("finding " + cls.getName() + " by example");
		try {
			getSession().clear();
			List<T> results = (List<T>) getSession().createCriteria(
					cls.getName()).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).add(create(exampleObj)).list();
			log.debug("find " + cls.getName()
					+ " by example successful, result size: " + results.size());
			return results;
		} catch (Exception re) {
			log.error("find " + cls.getName() + " by example failed", re);
			throw new RuntimeException(re);
		}
	}

	/**
	 * 获取所有对象列表
	 * 
	 * @param <T>
	 * @param cls
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> findAll(Class<T> cls) {
		log.debug("finding all " + cls.getName() + " instances");
		try {
			getSession().clear();
			String queryString = "from " + cls.getName();
			Query queryObject = getSession().createQuery(queryString);
			return queryObject.list();
		} catch (Exception re) {
			log.error("find all " + cls.getName() + " failed", re);
			throw new RuntimeException(re);
		}
	}
	
	/**
	 * ***********************************
	 * ********************************** 
	 * 分页查询查询
	 * ********************************** 
	 * **********************************
	 */
	/**
	 * 获取指定属性值的分页对象
	 */
	public <T> Page findByPropertyInPage(Class<T> cls, String key,
			Object value, int currentPage, int pageSize) {
		return findByPropertyInPageWithSort(cls, key, value, null, null, currentPage, pageSize);
	}
	
	/**
	 * 获取指定键值对的分页对象，按字段排序
	 * @param <T>
	 * @param cls
	 * @param key
	 * @param value
	 * @param sortKey
	 * @param sortType
	 * @param currentPage
	 * @param pageSize
	 * @return
	 */
	public <T> Page findByPropertyInPageWithSort(Class<T> cls, String key,
			Object value, String sortKey, String sortType, int currentPage, int pageSize) {
		log.debug("find " + cls.getName() + "by (" + key + "=" + value
				+ ") in page");
		try {
			Session session = getSession();
			session.clear();
			StringBuffer sb = new StringBuffer();
			sb.append("from ");
			sb.append(cls.getName());
			sb.append(" where ");
			sb.append(key);
			sb.append(" = ");
			sb.append(value);
			Query query = session.createQuery("select count(*) " + sb.toString());
			int totalRecords = Integer.parseInt(query.uniqueResult().toString());
			int startIndex = pageSize * (currentPage - 1);
			if(sortType != null) {
				if(sortType.equals(Constants.ASC)) {
					sb.append(" order by ");
					sb.append(sortKey);
					sb.append(" asc");
				} else {
					sb.append(" order by ");
					sb.append(sortKey);
					sb.append(" desc");
				}
			}
			query = session.createQuery(sb.toString());
			query.setFirstResult(startIndex);
			query.setMaxResults(pageSize);
			return new Page(pageSize, currentPage, totalRecords, query.list());
		} catch (Exception re) {
			log.error("find " + cls.getName() + " by property in page failed",
					re);
			return null;
		}
	}
	/**
	 * 通过Example获取分页对象
	 * @param <T>
	 * @param cls
	 * @param exampleObj
	 * @param currentPage
	 * @param pageSize
	 * @return
	 */
	public <T> Page findByExampleInPage(Class<T> cls, T exampleObj, String[] excludeProperties, int currentPage, int pageSize) {
		return findByExampleInPageWithSort(cls, exampleObj, excludeProperties, null, null, currentPage, pageSize);
	}
	
	/**
	 * 通过Example获取分页对象，并按字段排序
	 * @param <T>
	 * @param cls
	 * @param exampleObj
	 * @param sortKey
	 * @param sortType
	 * @param currentPage
	 * @param pageSize
	 * @return
	 */
	public <T> Page findByExampleInPageWithSort(Class<T> cls, T exampleObj, String[] excludeProperties, String sortKey, String sortType, int currentPage, int pageSize) {
		return findByExampleLikeInPageWithSort(cls, exampleObj, excludeProperties, sortKey, sortType, false, currentPage, pageSize);
	}
	
	/**
	 * 通过Example获取分页对象，并开启Like查询，按字段排序
	 * @param <T>
	 * @param cls
	 * @param exampleObj
	 * @param excludeProperties
	 * @param sortKey
	 * @param sortType
	 * @param isLike
	 * @param currentPage
	 * @param pageSize
	 * @return
	 */
	public <T> Page findByExampleLikeInPageWithSort(Class<T> cls, T exampleObj, String[] excludeProperties, String sortKey, String sortType, boolean isLike, int currentPage, int pageSize) {
		log.debug("find " + cls.getName() + " by example in page");
		try {
			Session session = getSession();
			session.clear();
			Example ex = create(exampleObj);
			ex.excludeZeroes();
			if(isLike) {
				ex.enableLike(MatchMode.ANYWHERE);
			}
			if(excludeProperties != null) {
				for(int i = 0; i < excludeProperties.length; i++) {
					ex.excludeProperty(excludeProperties[i]);
				}
			}
			int totalRecords = Integer.parseInt(session.createCriteria(
					cls.getName()).setProjection(Projections.rowCount()).add(ex).uniqueResult().toString());
			int startIndex = pageSize * (currentPage - 1);
			Criteria c = session.createCriteria(cls.getName()).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).add(ex).setFirstResult(startIndex).setMaxResults(pageSize);
			if(sortType != null) {
				if(sortType.equals(Constants.ASC)) {
					c.addOrder(Order.asc(sortKey));
				} else {
					c.addOrder(Order.desc(sortKey));
				}
			}
			return new Page(pageSize, currentPage, totalRecords, c.list());
		} catch (Exception re) {
			log.error("find " + cls.getName() + " by example in page failed",
					re);
			throw new RuntimeException(re);
		}
	}
	
	/**
	 * 通过分页方式获取对象列表
	 * @param <T>
	 * @param cls
	 * @param currentPage
	 * @param pageSize
	 * @return
	 */
	public <T> Page findAllInPage(Class<T> cls, int currentPage, int pageSize) {
		return findAllInPageWithSort(cls, null, null, currentPage, pageSize);
	}
	
	/**
	 * 获取对象列表，并按照指定排序策略进行排序
	 * @param <T>
	 * @param cls
	 * @param sortKey
	 * @param sortType
	 * @param currentPage
	 * @param pageSize
	 * @return
	 */
	public <T> Page findAllInPageWithSort(Class<T> cls, String sortKey, String sortType, int currentPage, int pageSize) {
		log.debug("finding all " + cls.getName() + " in page");
		try {
			Session session = getSession();
			session.clear();
			StringBuffer sb = new StringBuffer();
			sb.append("from ");
			sb.append(cls.getName());
			Query query = session.createQuery("select count(*) " + sb.toString());
			int totalRecords = Integer.valueOf(query.uniqueResult().toString());
			if(sortType != null) {
				if(sortType.equals(Constants.ASC)) {
					sb.append(" order by " + sortKey + " asc");
				} else {
					sb.append(" order by " + sortKey + " desc");
				}
			}
			query = session.createQuery(sb.toString());
			int startIndex = pageSize * (currentPage - 1);
			query.setFirstResult(startIndex);
			query.setMaxResults(pageSize);
			return new Page(pageSize, currentPage, totalRecords, query.list());
		} catch (Exception re) {
			log.error("find all " + cls.getName() + " in page failed", re);
			return null;
		}
	}
	
	/**
	 * ******************************
	 * ******************************
	 * 与时间相关查询
	 * ******************************
	 * ******************************
	 */
	
	/**
	 * 获取指定时间段内的分页对象
	 * @param <T>
	 * @param cls
	 * @param beforeDate
	 * @param afterDate
	 * @param currentPage
	 * @param pageSize
	 * @return
	 */
	public <T> Page findPageInDate(Class<T> cls, String timeKey, Date startDate, Date endDate, int currentPage,
			int pageSize) {
		return findPageInDateWithSort(cls, timeKey, null, startDate, endDate, currentPage, pageSize);
	}
	
	
	/**
	 * 获取指定时间段内的分页信息，并按照timeKey进行排序
	 * @param <T>
	 * @param cls
	 * @param timeKey
	 * @param sortType
	 * @param startDate
	 * @param endDate
	 * @param currentPage
	 * @param pageSize
	 * @return
	 */
	public <T> Page findPageInDateWithSort(Class<T> cls, String timeKey, String sortType, Date startDate, Date endDate, int currentPage,
			int pageSize) {
		log.debug("find log In Date with page ");
		try {
			Session session = getSession();
			session.clear();
			StringBuffer sb = new StringBuffer();
			sb.append("from ");
			sb.append(cls.getName());
			sb.append(" where ");
			sb.append(timeKey);
			sb.append(" >= ? and ");
			sb.append(timeKey);
			sb.append(" <= ?");
			Query query = session.createQuery("select count(*) " + sb.toString());
			query.setParameter(0, startDate);
			query.setParameter(1, endDate);
			int totalRecords = Integer.parseInt(query.uniqueResult().toString());
			int startIndex = pageSize * (currentPage - 1);
			if(sortType != null) {
				if(sortType.equals(Constants.ASC)) {
					sb.append(" order by ");
					sb.append(timeKey);
					sb.append(" asc");
				} else {
					sb.append(" order by ");
					sb.append(timeKey);
					sb.append(" desc");
				}
			}
			query = session.createQuery(sb.toString());
			query.setParameter(0, startDate);
			query.setParameter(1, endDate);
			query.setFirstResult(startIndex);
			query.setMaxResults(pageSize);
			return new Page(pageSize, currentPage, totalRecords, query.list());
		} catch (Exception re) {
			log.error("find log In Date with page failed", re);
			return null;
		}
	}
	
	/**
	 * 获取指定时间段内，key=value的分页信息，并按字段排序
	 * @param <T>
	 * @param cls
	 * @param key
	 * @param value
	 * @param timeKey
	 * @param sortType
	 * @param startDate
	 * @param endDate
	 * @param currentPage
	 * @param pageSize
	 * @return
	 */
	public <T> Page findByPropertyInDateWithSort(Class<T> cls, String key, Object value, String timeKey, String sortType, Date startDate, Date endDate, int currentPage,
			int pageSize) {
		log.debug("findByPropertyInDateWithSort");
		try {
			Session session = getSession();
			session.clear();
			StringBuffer sb = new StringBuffer();
			sb.append("from ");
			sb.append(cls.getName());
			sb.append(" where ");
			sb.append(key);
			sb.append(" = ? and ");
			sb.append(timeKey);
			sb.append(" >= ? and ");
			sb.append(timeKey);
			sb.append(" <= ?");
			Query query = session.createQuery("select count(*) " + sb.toString());
			query.setParameter(0, value);
			query.setParameter(1, startDate);
			query.setParameter(2, endDate);
			int totalRecords = Integer.parseInt(query.uniqueResult().toString());
			int startIndex = pageSize * (currentPage - 1);
			if(sortType != null) {
				if(sortType.equals(Constants.ASC)) {
					sb.append(" order by ");
					sb.append(timeKey);
					sb.append(" asc");
				} else {
					sb.append(" order by ");
					sb.append(timeKey);
					sb.append(" desc");
				}
			}
			query = session.createQuery(sb.toString());
			query.setParameter(0, value);
			query.setParameter(1, startDate);
			query.setParameter(2, endDate);
			query.setFirstResult(startIndex);
			query.setMaxResults(pageSize);
			return new Page(pageSize, currentPage, totalRecords, query.list());
		} catch (Exception re) {
			log.error("findByPropertyInDateWithSort", re);
			return null;
		}
	}
	
}
